/**
 * @file      udp.c
 * @author    wzzlyzdn (wzzlyzdn@163.com)
 * @brief     UDP 传输函数封装
 * @version   0.1
 * @date      2022-08-16
 *
 * @copyright Copyright (c) 2022 wzzlyzdn
 *
 * @note      历史记录:
 *            -
 * @warning
 * @par       修改记录:
 * <table>
 * <tr><th>date          <th>Version    <th>Author      <th>Description    </tr>
 * <tr><td>2022-08-16    <td> 0.1       <td>wzzlyzdn       <td>创建初始版本    </tr>
 * </table>
 */

#include "server.h"
#include "udp.h"
#include "server_client.h"

FILE_INFO filesend1;
FILE_INFO filesend2;

/**
 * @fn        int UDP_BIND(int sockfd, struct sockaddr_in socket_addr)
 * @brief     UPD 绑定函数封装
 *
 * @param     [in] sockfd       绑定的 sockfd
 * @param     [in] socket_addr  绑定地址端口信息
 *
 * @return    int               失败返回 -1；成功返回 0；
 */
int UDP_BIND(int sockfd, struct sockaddr_in socket_addr)
{
    DEBUG("绑定\n");
    int ret_bind = bind(sockfd, (struct sockaddr *)&socket_addr, sizeof(socket_addr));
    if (ret_bind < 0)
    {
        DEBUG("绑定失败\n");
        return -1;
    }
    else
    {
        DEBUG("绑定成功\n");
        return 0;
    }
}

/**
 * @fn        int UDP_Init(UDP_INFO udp, SOCKPACK *sock_pack)
 * @brief     UDP 初始化
 *
 * @param     [in]  udp       UDP_INFO 结构体，传入 ip port等信息
 * @param     [out] sock_pack 传出配置好的信息
 *
 * @return    int             失败返回 -1；成功返回 创建的 sockfd；
 */
int UDP_Init(UDP_INFO udp, SOCKPACK *sock_pack)
{
    DEBUG_INFO("ip: %s\nport: %d\nUI: %d\n", udp.ip, udp.port, udp.id);

    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in socket_addr;

    if (-1 == sockfd)
    {
        DEBUG("sockfd 创建失败\n");
        return -1;
    }

    bzero(&socket_addr, sizeof(struct sockaddr_in));
    socket_addr.sin_family = AF_INET;
    socket_addr.sin_port = htons(udp.port);

    if (SERVER_ID == udp.id)
    {
        DEBUG("server\n");
        socket_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        if (UDP_BIND(sockfd, socket_addr))
        {
            DEBUG("err : bing 失败\n");
            return -1;
        }
    }
    else if (CLIENT_ID == udp.id)
    {
        DEBUG("client\n");
        socket_addr.sin_addr.s_addr = inet_addr(udp.ip);
    }
    else
    {
        DEBUG("err : SERVER OR CLIENT ID 错误\n");
    }

    DEBUG_INFO("socket_addr.sin_family: %d, socket_addr.sin_port: %d, socket_addr.sin__addr.s_addr: %s\n",
          socket_addr.sin_family, ntohs(socket_addr.sin_port), inet_ntoa(socket_addr.sin_addr));

    sock_pack->sockfd = sockfd;
    sock_pack->addr = socket_addr;

    DEBUG_INFO("addr->sin_family: %d, addr->sin_port: %d, addr->sin_addr.s_addr: %s\n",
          sock_pack->addr.sin_family, ntohs(sock_pack->addr.sin_port), inet_ntoa(sock_pack->addr.sin_addr));

    DEBUG_INFO("sockfd = %d\n", sockfd);

    return sockfd;
}

/**
 * @fn        void *UDP_Recv(void *arg)
 * @brief     线程函数 - UDP 发送
 *
 * @param     [in] arg       SOCKPACK 结构体，存储 ip port 信息
 *
 * @return    void*
 */
void *UDP_Recv(void *arg)
{
    SOCKPACK *sock_pack = (SOCKPACK *)arg;
    int sockfd = sock_pack->sockfd;
    UDP_INFO client_ip;
    struct sockaddr_in socket_addr = sock_pack->addr;
    struct sockaddr_in my_addr;
    socklen_t socklen = sizeof(my_addr);
    char buf[BUFFER_SIZE];
    bzero(&buf, sizeof(buf));
    int recvnum;

    DEBUG_INFO("socket_addr.sin_family: %d, socket_addr.sin_port: %d, socket_addr.sin__addr.s_addr: %s\n",
          socket_addr.sin_family, ntohs(socket_addr.sin_port), inet_ntoa(socket_addr.sin_addr));
    if (-1 == sockfd)
    {
        DEBUG("sockfd 创建失败\n");
        pthread_exit(NULL);
    }

    while (1)
    {
        memset(&buf, 0, sizeof(buf));
        recvnum = recvfrom(sockfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&my_addr, &socklen);
        DEBUG("【%s:%d】 %s\n", inet_ntoa(my_addr.sin_addr), ntohs(my_addr.sin_port), buf);
        
        // if (!strncmp(buf, "$IP", sizeof("$IP")))
        // {
        //     save_udp_info(&client_ip, my_addr);

        //     DEBUG("-----------------\n");

        //     DEBUG("family: %d, port: %d, ip: %s\n",
        //           my_addr.sin_family, ntohs(my_addr.sin_port), inet_ntoa(my_addr.sin_addr));
        //     DEBUG("send xiaoxi\n");
        //     sendto(sockfd, "ret!!!\n", strlen("ret!!!\n"), 0, (struct sockaddr *)&my_addr, sizeof(my_addr));
        //     DEBUG("-----------------\n");
        // }

        if (!strncmp(buf, "quit", sizeof("quit")))
        {
            DEBUG("程序退出\n");
            break;
        }
    }
    close(sockfd);
    pthread_exit(NULL);
}

/**
 * @fn        void *UDP_Send(void *arg)
 * @brief     线程函数 - UDP 发送
 *
 * @param     [in] arg       SOCKPACK 结构体，存储 ip port 信息
 *
 * @return    void*
 */
void *UDP_Send(void *arg)
{

    SOCKPACK *sock_pack = (SOCKPACK *)arg;
    int sockfd = sock_pack->sockfd;

    struct sockaddr_in socket_addr = sock_pack->addr;
    struct sockaddr_in my_addr;
    socklen_t len = sizeof(my_addr);

    if (-1 == sockfd)
    {
        DEBUG("sockfd 创建失败\n");
        pthread_exit(NULL);
    }
    char buf[BUFFER_SIZE];
    bzero(&buf, sizeof(buf));

    int socklen;
    int sendnum;
    while (1)
    {
        bzero(&buf, sizeof(buf));
        scanf("%s", buf);
        sendnum = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&socket_addr, sizeof(socket_addr));
        if (-1 == sendnum)
        {
            DEBUG("err : sendto\n");
            break;
        }
        if (!strncmp(buf, "quit", sizeof("quit")))
        {
            DEBUG("程序退出\n");
            break;
        }
    }
    close(sockfd);
    pthread_exit(NULL);
}

/**
 * @fn        void save_udp_info(UDP_INFO *info, struct sockaddr_in my_addr)
 * @brief     保存目标 IP 地址，端口信息
 *
 * @param     [out] info      UDP_INFO 结构体，用于存储 IP 端口 信息
 * @param     [in]  my_addr   传入 sockaddr_in 类型结构体 my_addr，从中提取信息
 *
 */
void save_udp_info(UDP_INFO *info, struct sockaddr_in my_addr)
{
    memset(info, 0, sizeof(*info));

    sprintf(info->ip, "%s", inet_ntoa(my_addr.sin_addr));
    info->port = ntohs(my_addr.sin_port);

    DEBUG_INFO("family: %d, port: %d, ip: %s\n",
          info->id, info->port, info->ip);
}